/*
 * (C) Copyright 2001-2010
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <command.h>
#include <net.h>
#include <miiphy.h>
#include <phy.h>


void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
{
	char *end;
	int i;

	for (i = 0; i < 6; ++i) {
		enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
		if (addr)
			addr = (*end) ? end + 1 : end;
	}
}

int eth_getenv_enetaddr(char *name, uchar *enetaddr)
{
	eth_parse_enetaddr(getenv(name), enetaddr);
	return is_valid_ether_addr(enetaddr);
}

int eth_setenv_enetaddr(char *name, const uchar *enetaddr)
{
	char buf[20];

	sprintf(buf, "%pM", enetaddr);

	return setenv(name, buf);
}

int eth_getenv_enetaddr_by_index(const char *base_name, int index,
				 uchar *enetaddr)
{
	char enetvar[32];
	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
	return eth_getenv_enetaddr(enetvar, enetaddr);
}

static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index,
				 uchar *enetaddr)
{
	char enetvar[32];
	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
	return eth_setenv_enetaddr(enetvar, enetaddr);
}


static int eth_mac_skip(int index)
{
	char enetvar[15];
	char *skip_state;
	sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
	return ((skip_state = getenv(enetvar)) != NULL);
}

#ifdef CONFIG_RANDOM_MACADDR
void eth_random_enetaddr(uchar *enetaddr)
{
	uint32_t rval;

	srand(get_timer(0));

	rval = rand();
	enetaddr[0] = rval & 0xff;
	enetaddr[1] = (rval >> 8) & 0xff;
	enetaddr[2] = (rval >> 16) & 0xff;

	rval = rand();
	enetaddr[3] = rval & 0xff;
	enetaddr[4] = (rval >> 8) & 0xff;
	enetaddr[5] = (rval >> 16) & 0xff;

	/* make sure it's local and unicast */
	enetaddr[0] = (enetaddr[0] | 0x02) & ~0x01;
}
#endif

/*
 * CPU and board-specific Ethernet initializations.  Aliased function
 * signals caller to move on
 */
static int __def_eth_init(bd_t *bis)
{
	return -1;
}
int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));

#ifdef CONFIG_API
static struct {
	uchar data[PKTSIZE];
	int length;
} eth_rcv_bufs[PKTBUFSRX];

static unsigned int eth_rcv_current, eth_rcv_last;
#endif

static struct eth_device *eth_devices;
struct eth_device *eth_current;

struct eth_device *eth_get_dev_by_name(const char *devname)
{
	struct eth_device *dev, *target_dev;

	BUG_ON(devname == NULL);

	if (!eth_devices)
		return NULL;

	dev = eth_devices;
	target_dev = NULL;
	do {
		if (strcmp(devname, dev->name) == 0) {
			target_dev = dev;
			break;
		}
		dev = dev->next;
	} while (dev != eth_devices);

	return target_dev;
}

struct eth_device *eth_get_dev_by_index(int index)
{
	struct eth_device *dev, *target_dev;

	if (!eth_devices)
		return NULL;

	dev = eth_devices;
	target_dev = NULL;
	do {
		if (dev->index == index) {
			target_dev = dev;
			break;
		}
		dev = dev->next;
	} while (dev != eth_devices);

	return target_dev;
}

int eth_get_dev_index(void)
{
	if (!eth_current)
		return -1;

	return eth_current->index;
}

static void eth_current_changed(void)
{
	char *act = getenv("ethact");
	/* update current ethernet name */
	if (eth_current) {
		if (act == NULL || strcmp(act, eth_current->name) != 0)
			setenv("ethact", eth_current->name);
	}
	/*
	 * remove the variable completely if there is no active
	 * interface
	 */
	else if (act != NULL)
		setenv("ethact", NULL);
}

int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
		   int eth_number)
{
	unsigned char env_enetaddr[6];
	int ret = 0;

	eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);

	if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) {
		if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) &&
				memcmp(dev->enetaddr, env_enetaddr, 6)) {
			printf("\nWarning: %s MAC addresses don't match:\n",
				dev->name);
			printf("Address in SROM is         %pM\n",
				dev->enetaddr);
			printf("Address in environment is  %pM\n",
				env_enetaddr);
		}

		memcpy(dev->enetaddr, env_enetaddr, 6);
	} else if (is_valid_ether_addr(dev->enetaddr)) {
		eth_setenv_enetaddr_by_index(base_name, eth_number,
					     dev->enetaddr);
		printf("\nWarning: %s using MAC address from net device\n",
			dev->name);
	}

	if (dev->write_hwaddr &&
			!eth_mac_skip(eth_number)) {
		if (!is_valid_ether_addr(dev->enetaddr))
			return -1;

		ret = dev->write_hwaddr(dev);
	}

	return ret;
}

int eth_register(struct eth_device *dev)
{
	struct eth_device *d;
	static int index;

	assert(strlen(dev->name) < sizeof(dev->name));

	if (!eth_devices) {
		eth_current = eth_devices = dev;
		eth_current_changed();
	} else {
		for (d = eth_devices; d->next != eth_devices; d = d->next)
			;
		d->next = dev;
	}

	dev->state = ETH_STATE_INIT;
	dev->next  = eth_devices;
	dev->index = index++;

	return 0;
}

int eth_unregister(struct eth_device *dev)
{
	struct eth_device *cur;

	/* No device */
	if (!eth_devices)
		return -1;

	for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
	     cur = cur->next)
		;

	/* Device not found */
	if (cur->next != dev)
		return -1;

	cur->next = dev->next;

	if (eth_devices == dev)
		eth_devices = dev->next == eth_devices ? NULL : dev->next;

	if (eth_current == dev) {
		eth_current = eth_devices;
		eth_current_changed();
	}

	return 0;
}

static void eth_env_init(bd_t *bis)
{
	const char *s;

	if ((s = getenv("bootfile")) != NULL)
		copy_filename(BootFile, s, sizeof(BootFile));
}

int eth_initialize(bd_t *bis)
{
	int num_devices = 0;
	eth_devices = NULL;
	eth_current = NULL;

	bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
	miiphy_init();
#endif

#ifdef CONFIG_PHYLIB
	phy_init();
#endif

	eth_env_init(bis);

	/*
	 * If board-specific initialization exists, call it.
	 * If not, call a CPU-specific one
	 */
	if (board_eth_init != __def_eth_init) {		
		if (board_eth_init(bis) < 0)
			printf("Board Net Initialization Failed\n");
	} else if (cpu_eth_init != __def_eth_init) {		
		if (cpu_eth_init(bis) < 0)
			printf("CPU Net Initialization Failed\n");
	} else
		printf("Net Initialization Skipped\n");

	if (!eth_devices) {
		puts("No ethernet found.\n");
		bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
	} else {
		struct eth_device *dev = eth_devices;
		char *ethprime = getenv("ethprime");

		bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
		do {
			if (dev->index)
				puts(", ");

			printf("%s", dev->name);

			if (ethprime && strcmp(dev->name, ethprime) == 0) {
				eth_current = dev;
				puts(" [PRIME]");
			}

			if (strchr(dev->name, ' '))
				puts("\nWarning: eth device name has a space!"
					"\n");

			if (eth_write_hwaddr(dev, "eth", dev->index))
				puts("\nWarning: failed to set MAC address\n");

			dev = dev->next;
			num_devices++;
		} while (dev != eth_devices);

		eth_current_changed();
		putc('\n');
	}

	return num_devices;
}

#ifdef CONFIG_MCAST_TFTP
/* Multicast.
 * mcast_addr: multicast ipaddr from which multicast Mac is made
 * join: 1=join, 0=leave.
 */
int eth_mcast_join(IPaddr_t mcast_ip, u8 join)
{
	u8 mcast_mac[6];
	if (!eth_current || !eth_current->mcast)
		return -1;
	mcast_mac[5] = htonl(mcast_ip) & 0xff;
	mcast_mac[4] = (htonl(mcast_ip)>>8) & 0xff;
	mcast_mac[3] = (htonl(mcast_ip)>>16) & 0x7f;
	mcast_mac[2] = 0x5e;
	mcast_mac[1] = 0x0;
	mcast_mac[0] = 0x1;
	return eth_current->mcast(eth_current, mcast_mac, join);
}

/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
 * and this is the ethernet-crc method needed for TSEC -- and perhaps
 * some other adapter -- hash tables
 */
#define CRCPOLY_LE 0xedb88320
u32 ether_crc(size_t len, unsigned char const *p)
{
	int i;
	u32 crc;
	crc = ~0;
	while (len--) {
		crc ^= *p++;
		for (i = 0; i < 8; i++)
			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
	}
	/* an reverse the bits, cuz of way they arrive -- last-first */
	crc = (crc >> 16) | (crc << 16);
	crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
	crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
	crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
	crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
	return crc;
}

#endif


int eth_init(bd_t *bis)
{
	struct eth_device *old_current, *dev;

	if (!eth_current) {
		puts("No ethernet found.\n");
		return -1;
	}

	/* Sync environment with network devices */
	dev = eth_devices;
	do {
		uchar env_enetaddr[6];

		if (eth_getenv_enetaddr_by_index("eth", dev->index,
						 env_enetaddr))
			memcpy(dev->enetaddr, env_enetaddr, 6);

		dev = dev->next;
	} while (dev != eth_devices);

	old_current = eth_current;
	do {
		debug("Trying %s\n", eth_current->name);

		if (eth_current->init(eth_current, bis) >= 0) {
			eth_current->state = ETH_STATE_ACTIVE;

			return 0;
		}
		debug("FAIL\n");

		eth_try_another(0);
	} while (old_current != eth_current);

	return -1;
}

void eth_halt(void)
{
	if (!eth_current)
		return;

	eth_current->halt(eth_current);

	eth_current->state = ETH_STATE_PASSIVE;
}

int eth_send(void *packet, int length)
{
	if (!eth_current)
		return -1;

	return eth_current->send(eth_current, packet, length);
}

int eth_rx(void)
{
	if (!eth_current)
		return -1;

	return eth_current->recv(eth_current);
}

#ifdef CONFIG_API
static void eth_save_packet(void *packet, int length)
{
	char *p = packet;
	int i;

	if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
		return;

	if (PKTSIZE < length)
		return;

	for (i = 0; i < length; i++)
		eth_rcv_bufs[eth_rcv_last].data[i] = p[i];

	eth_rcv_bufs[eth_rcv_last].length = length;
	eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
}

int eth_receive(void *packet, int length)
{
	char *p = packet;
	void *pp = push_packet;
	int i;

	if (eth_rcv_current == eth_rcv_last) {
		push_packet = eth_save_packet;
		eth_rx();
		push_packet = pp;

		if (eth_rcv_current == eth_rcv_last)
			return -1;
	}

	length = min(eth_rcv_bufs[eth_rcv_current].length, length);

	for (i = 0; i < length; i++)
		p[i] = eth_rcv_bufs[eth_rcv_current].data[i];

	eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
	return length;
}
#endif /* CONFIG_API */

void eth_try_another(int first_restart)
{
	static struct eth_device *first_failed;
	char *ethrotate;

	/*
	 * Do not rotate between network interfaces when
	 * 'ethrotate' variable is set to 'no'.
	 */
	ethrotate = getenv("ethrotate");
	if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
		return;

	if (!eth_current)
		return;

	if (first_restart)
		first_failed = eth_current;

	eth_current = eth_current->next;

	eth_current_changed();

	if (first_failed == eth_current)
		NetRestartWrap = 1;
}

void eth_set_current(void)
{
	static char *act;
	static int  env_changed_id;
	struct eth_device *old_current;
	int	env_id;

	if (!eth_current)	/* XXX no current */
		return;

	env_id = get_env_id();
	if ((act == NULL) || (env_changed_id != env_id)) {
		act = getenv("ethact");
		env_changed_id = env_id;
	}
	if (act != NULL) {
		old_current = eth_current;
		do {
			if (strcmp(eth_current->name, act) == 0)
				return;
			eth_current = eth_current->next;
		} while (old_current != eth_current);
	}

	eth_current_changed();
}

char *eth_get_name(void)
{
	return eth_current ? eth_current->name : "unknown";
}

/*---------------------------------------------------------------------*/
#define XSPIPS_CR_OFFSET	0x00  /**< Configuration */
#define XSPIPS_SR_OFFSET	0x04  /**< Interrupt Status */
#define XSPIPS_IER_OFFSET	0x08  /**< Interrupt Enable */
#define XSPIPS_IDR_OFFSET	0x0c  /**< Interrupt Disable */
#define XSPIPS_IMR_OFFSET	0x10  /**< Interrupt Enabled Mask */
#define XSPIPS_ER_OFFSET	0x14  /**< Enable/Disable Register */
#define XSPIPS_DR_OFFSET	0x18  /**< Delay Register */
#define XSPIPS_TXD_OFFSET	0x1C  /**< Data Transmit Register */
#define XSPIPS_RXD_OFFSET	0x20  /**< Data Receive Register */
#define XSPIPS_SICR_OFFSET	0x24  /**< Slave Idle Count */
#define XSPIPS_TXWR_OFFSET	0x28  /**< Transmit FIFO Watermark */
#define XSPIPS_RXWR_OFFSET	0x2C  /**< Receive FIFO Watermark */

#define XSPIPS_MASTER_OPTION		0x1  /**< Master mode option */
#define XSPIPS_CLK_ACTIVE_LOW_OPTION	0x2  /**< Active Low Clock option */
#define XSPIPS_CLK_PHASE_1_OPTION	0x4  /**< Clock Phase one option */
#define XSPIPS_DECODE_SSELECT_OPTION	0x8  /**< Select 16 slaves Option */
#define XSPIPS_FORCE_SSELECT_OPTION	0x10 /**< Force Slave Select */
#define XSPIPS_MANUAL_START_OPTION	0x20 /**< Manual Start mode option */

#define XSPIPS_CR_MODF_GEN_EN_MASK 0x00020000 /**< Modefail Generation
						 Enable */
#define XSPIPS_CR_MANSTRT_MASK   0x00010000 /**< Manual Transmission Start */
#define XSPIPS_CR_MANSTRTEN_MASK 0x00008000 /**< Manual Transmission Start
						 Enable */
#define XSPIPS_CR_SSFORCE_MASK   0x00004000 /**< Force Slave Select */
#define XSPIPS_CR_SSCTRL_MASK    0x00003C00 /**< Slave Select Decode */
#define XSPIPS_CR_SSCTRL_SHIFT   10	    /**< Slave Select Decode shift */
#define XSPIPS_CR_SSCTRL_MAXIMUM 0xF	    /**< Slave Select maximum value */
#define XSPIPS_CR_SSDECEN_MASK   0x00000200 /**< Slave Select Decode Enable */

#define XSPIPS_CR_PRESC_MASK     0x00000038 /**< Prescaler Setting */
#define XSPIPS_CR_PRESC_SHIFT    3	    /**< Prescaler shift */
#define XSPIPS_CR_PRESC_MAXIMUM  0x07	    /**< Prescaler maximum value */

#define XSPIPS_CR_CPHA_MASK      0x00000004 /**< Phase Configuration */
#define XSPIPS_CR_CPOL_MASK      0x00000002 /**< Polarity Configuration */

#define XSPIPS_CR_MSTREN_MASK    0x00000001 /**< Master Mode Enable */
#define XSPIPS_CR_RESET_STATE    0x00020000 /**< Mode Fail Generation Enable */

#define XSPIPS_IXR_TXUF_MASK		0x00000040  /**< Tx FIFO Underflow */
#define XSPIPS_IXR_RXFULL_MASK		0x00000020  /**< Rx FIFO Full */
#define XSPIPS_IXR_RXNEMPTY_MASK	0x00000010  /**< Rx FIFO Not Empty */
#define XSPIPS_IXR_TXFULL_MASK		0x00000008  /**< Tx FIFO Full */
#define XSPIPS_IXR_TXOW_MASK		0x00000004  /**< Tx FIFO Overwater */
#define XSPIPS_IXR_MODF_MASK		0x00000002  /**< Mode Fault */
#define XSPIPS_IXR_RXOVR_MASK		0x00000001  /**< Rx FIFO Overrun */
#define XSPIPS_IXR_DFLT_MASK		0x00000027  /**< Default interrupts
							 mask */
#define XSPIPS_IXR_WR_TO_CLR_MASK	0x00000043  /**< Interrupts which
							 need write to clear */
#define XSPIPS_ISR_RESET_STATE		0x04	    /**< Default to tx/rx
						       * reg empty */
#define XSPIPS_IXR_DISABLE_ALL_MASK	0x00000043  /**< Disable all
						       * interrupts */

#define XSPIPS_FIFO_DEPTH	128 /**< FIFO depth of Tx and Rx */

#define NREAD	(0x60)
#define NWRITE	(0x61)

#define SIO (0xF0)
#define STS (0xFE)
#define SPG (0xFF)

#define SPIF	(0x80)
#define RACK	(0x20)
#define RXRDY	(0x02)
#define TXRDY	(0x01)

#define XST_SUCCESS                     0L
#define XST_FAILURE                     1L

#define XSpiPs_WriteReg(BaseAddress, RegOffset, RegisterValue) \
	*(volatile u32 *) ((BaseAddress) + RegOffset) = RegisterValue

#define XSpiPs_ReadReg(BaseAddress, RegOffset) \
	(*(volatile u32 * )((BaseAddress) + (RegOffset)))


#define XSpiPs_Enable(InstancePtr)					\
	*(volatile u32 *)((InstancePtr->Config.BaseAddress) + XSPIPS_ER_OFFSET) = 1

#define XSpiPs_Disable(InstancePtr)					\
	*(volatile u32 *)((InstancePtr->Config.BaseAddress) + XSPIPS_ER_OFFSET) = 0

typedef void (*XSpiPs_StatusHandler) (void *CallBackRef, u32 StatusEvent,
					unsigned ByteCount);


typedef struct {
	u32 Option;
	u32 Mask;
} OptionsMap;

static OptionsMap OptionsTable[] = {
	{XSPIPS_MASTER_OPTION, XSPIPS_CR_MSTREN_MASK},
	{XSPIPS_CLK_ACTIVE_LOW_OPTION, XSPIPS_CR_CPOL_MASK},
	{XSPIPS_CLK_PHASE_1_OPTION, XSPIPS_CR_CPHA_MASK},
	{XSPIPS_DECODE_SSELECT_OPTION, XSPIPS_CR_SSDECEN_MASK},
	{XSPIPS_FORCE_SSELECT_OPTION, XSPIPS_CR_SSFORCE_MASK},
	{XSPIPS_MANUAL_START_OPTION, XSPIPS_CR_MANSTRTEN_MASK}
};

#define XSPIPS_NUM_OPTIONS	(sizeof(OptionsTable) / sizeof(OptionsMap))

typedef struct {
	u16 DeviceId;		/**< Unique ID  of device */
	u32 BaseAddress;	/**< Base address of the device */
	u32 InputClockHz;	/**< Input clock frequency */
} XSpiPs_Config;


typedef struct {
	XSpiPs_Config Config;	 /**< Configuration structure */
	u32 IsReady;		 /**< Device is initialized and ready */

	u8 *SendBufferPtr;	 /**< Buffer to send (state) */
	u8 *RecvBufferPtr;	 /**< Buffer to receive (state) */
	unsigned RequestedBytes; /**< Number of bytes to transfer (state) */
	unsigned RemainingBytes; /**< Number of bytes left to transfer(state) */
	u32 IsBusy;		 /**< A transfer is in progress (state) */
	u32 SlaveSelect;     /**< The slave select value when
					 XSPIPS_FORCE_SSELECT_OPTION is set */

	XSpiPs_StatusHandler StatusHandler;
	void *StatusRef;  	 /**< Callback reference for status handler */

} XSpiPs;

XSpiPs_Config XSpiPs_ConfigTable[] =
{
	{
		0,
		0xE0006000,
		166666672
	}
};


XSpiPs_Config *g_SpiConfig;

void Spi_SetOptions(XSpiPs *InstancePtr, u32 Options)
{
	u32 ConfigReg;
	unsigned int Index;
	u32 CurrentConfigReg;	

	/*
	 * Do not allow the slave select to change while a transfer is in
	 * progress. Not thread-safe.
	 */
	if (InstancePtr->IsBusy) {
		return;
	}

	ConfigReg = XSpiPs_ReadReg(InstancePtr->Config.BaseAddress,
				 XSPIPS_CR_OFFSET);

	CurrentConfigReg = ConfigReg;

	/*
	 * Loop through the options table, turning the option on or off
	 * depending on whether the bit is set in the incoming options flag.
	 */
	for (Index = 0; Index < XSPIPS_NUM_OPTIONS; Index++) {
		if (Options & OptionsTable[Index].Option) {
			/* Turn it on */
			ConfigReg |= OptionsTable[Index].Mask;
		}
		else {
			/* Turn it off */
			ConfigReg &= ~(OptionsTable[Index].Mask);
		}
	}


	/*
	 * If CPOL-CPHA bits are toggled from previous state,
	 * disable before writing the configuration register and then enable.
	 */
	if( ((CurrentConfigReg & XSPIPS_CR_CPOL_MASK) !=
		(ConfigReg & XSPIPS_CR_CPOL_MASK)) ||
		((CurrentConfigReg & XSPIPS_CR_CPHA_MASK) !=
		(ConfigReg & XSPIPS_CR_CPHA_MASK)) ) {
			XSpiPs_WriteReg((InstancePtr->Config.BaseAddress), XSPIPS_ER_OFFSET, 0);
		}

	/*
	 * Now write the Config register. Leave it to the upper layers
	 * to restart the device.
	 */
	XSpiPs_WriteReg(InstancePtr->Config.BaseAddress,
				XSPIPS_CR_OFFSET, ConfigReg);

	/*
	 * Enable
	 */
	if( ((CurrentConfigReg & XSPIPS_CR_CPOL_MASK) !=
		(ConfigReg & XSPIPS_CR_CPOL_MASK)) ||
		((CurrentConfigReg & XSPIPS_CR_CPHA_MASK) !=
		(ConfigReg & XSPIPS_CR_CPHA_MASK)) ) {
			XSpiPs_WriteReg((InstancePtr->Config.BaseAddress), XSPIPS_ER_OFFSET, 1);
		}
}


u32 Spi_GetOptions(XSpiPs *InstancePtr)
{
	u32 OptionsFlag = 0;
	u32 ConfigReg;
	unsigned int Index;	

	/*
	 * Get the current options
	 */
	ConfigReg =
		XSpiPs_ReadReg(InstancePtr->Config.BaseAddress,
				 XSPIPS_CR_OFFSET);

	/*
	 * Loop through the options table to grab options
	 */
	for (Index = 0; Index < XSPIPS_NUM_OPTIONS; Index++) {
		if (ConfigReg & OptionsTable[Index].Mask) {
			OptionsFlag |= OptionsTable[Index].Option;
		}
	}

	return OptionsFlag;
}

#define XSpiPs_IsMaster(InstancePtr) \
		((Spi_GetOptions(InstancePtr) & \
		  XSPIPS_MASTER_OPTION) ? 1 : 0)

#define XSpiPs_IsDecodeSSelect(InstancePtr) \
		((Spi_GetOptions(InstancePtr) & \
		  XSPIPS_DECODE_SSELECT_OPTION) ? 1 : 0)

#define XSpiPs_IsManualStart(InstancePtr) \
		((Spi_GetOptions(InstancePtr) & \
		  XSPIPS_MANUAL_START_OPTION) ? 1 : 0)

#define XSpiPs_IsManualChipSelect(InstancePtr) \
		((Spi_GetOptions(InstancePtr) & \
		  XSPIPS_FORCE_SSELECT_OPTION) ? 1 : 0)

#define XSpiPs_SendByte(BaseAddress, Data) \
		*(volatile u32 *)((BaseAddress) + XSPIPS_TXD_OFFSET) = Data;

#define XSpiPs_RecvByte(BaseAddress) \
		(u8)(*(volatile u32 * )((BaseAddress) + XSPIPS_RXD_OFFSET))

void XSpiPs_SetSlaveSelect(XSpiPs *InstancePtr, u8 SlaveSel)
{
	u32 ConfigReg;	

	/*
	 * Do not allow the slave select to change while a transfer is in
	 * progress. Not thread-safe.
	 */
	if (InstancePtr->IsBusy) {
		return;
	}
	/*
	 * If decode slave select option is set,
	 * then set slave select value directly.
	 * Update the Instance structure member.
	 */
	if ( XSpiPs_IsDecodeSSelect( InstancePtr ) ) {
		InstancePtr->SlaveSelect = SlaveSel << XSPIPS_CR_SSCTRL_SHIFT;
	}
	else {
	/*
	 * Set the bit position to low using SlaveSel. Update the Instance
	 * structure member.
	 */
		InstancePtr->SlaveSelect = ((~(1 << SlaveSel)) & \
			XSPIPS_CR_SSCTRL_MAXIMUM) << XSPIPS_CR_SSCTRL_SHIFT;
	}

	/*
	 * Read the config register, update the slave select value and write
	 * back to config register.
	 */
	ConfigReg = XSpiPs_ReadReg(InstancePtr->Config.BaseAddress,
			 XSPIPS_CR_OFFSET);
	ConfigReg &= (~XSPIPS_CR_SSCTRL_MASK);
	ConfigReg |= InstancePtr->SlaveSelect;
	XSpiPs_WriteReg(InstancePtr->Config.BaseAddress, XSPIPS_CR_OFFSET,
			 ConfigReg);

	
}

void XSpiPs_SetClkPrescaler(XSpiPs *InstancePtr, u8 Prescaler)
{
	u32 ConfigReg;	

	/*
	 * Do not allow the prescaler to be changed while a transfer is in
	 * progress. Not thread-safe.
	 */
	if (InstancePtr->IsBusy) {
		return;
	}

	/*
	 * Read the Config register, mask out the interesting bits, and set
	 * them with the shifted value passed into the function. Write the
	 * results back to the Config register.
	 */
	ConfigReg = XSpiPs_ReadReg(InstancePtr->Config.BaseAddress,
				 XSPIPS_CR_OFFSET);

	ConfigReg &= ~XSPIPS_CR_PRESC_MASK;
	ConfigReg |= (u32) (Prescaler & XSPIPS_CR_PRESC_MAXIMUM) <<
		XSPIPS_CR_PRESC_SHIFT;

	XSpiPs_WriteReg(InstancePtr->Config.BaseAddress,
			XSPIPS_CR_OFFSET,
			ConfigReg);	
}

int XSpiPs_PolledTransfer(XSpiPs *InstancePtr, u8 *SendBufPtr,
				u8 *RecvBufPtr, u32 ByteCount)
{
	u32 StatusReg;
	u32 ConfigReg;
	u32 TransCount;

	/*
	 * Check whether there is another transfer in progress. Not thread-safe.
	 */
	if (InstancePtr->IsBusy) {
		return 1;
	}

	/*
	 * Set the busy flag, which will be cleared when the transfer is
	 * entirely done.
	 */
	InstancePtr->IsBusy = 1;

	/*
	 * Set up buffer pointers.
	 */
	InstancePtr->SendBufferPtr = SendBufPtr;
	InstancePtr->RecvBufferPtr = RecvBufPtr;

	InstancePtr->RequestedBytes = ByteCount;
	InstancePtr->RemainingBytes = ByteCount;	
	/*
	 * If manual chip select mode, initialize the slave select value.
	 */
	if (XSpiPs_IsManualChipSelect(InstancePtr)) {
		ConfigReg = XSpiPs_ReadReg(InstancePtr->Config.BaseAddress,
					 XSPIPS_CR_OFFSET);
		/*
		 * Set the slave select value.
		 */
		ConfigReg &= ~XSPIPS_CR_SSCTRL_MASK;
		ConfigReg |= InstancePtr->SlaveSelect;
		XSpiPs_WriteReg(InstancePtr->Config.BaseAddress,
				 XSPIPS_CR_OFFSET, ConfigReg);
	}

	/*
	 * Enable the device.
	 */
	XSpiPs_Enable(InstancePtr);
	while((InstancePtr->RemainingBytes > 0) ||
		(InstancePtr->RequestedBytes > 0)) {
		TransCount = 0;
		/*
		 * Fill the TXFIFO with as many bytes as it will take (or as
		 * many as we have to send).
		 */
		while ((InstancePtr->RemainingBytes > 0) &&
			(TransCount < XSPIPS_FIFO_DEPTH)) {
			XSpiPs_SendByte(InstancePtr->Config.BaseAddress,
					*InstancePtr->SendBufferPtr);
			InstancePtr->SendBufferPtr++;
			InstancePtr->RemainingBytes--;
			++TransCount;
		}

		/*
		 * If master mode and manual start mode, issue manual start
		 * command to start the transfer.
		 */
		if (XSpiPs_IsManualStart(InstancePtr)
			&& XSpiPs_IsMaster(InstancePtr)) {
			ConfigReg = XSpiPs_ReadReg(
					InstancePtr->Config.BaseAddress,
					 XSPIPS_CR_OFFSET);
			ConfigReg |= XSPIPS_CR_MANSTRT_MASK;
//printf("reg:0x%x  data = 0x%x.\n",XSPIPS_CR_OFFSET,ConfigReg);
			XSpiPs_WriteReg(InstancePtr->Config.BaseAddress,
					 XSPIPS_CR_OFFSET, ConfigReg);
		}
		/*
		 * Wait for the transfer to finish by polling Tx fifo status.
		 */
		do {
			StatusReg = XSpiPs_ReadReg(
					InstancePtr->Config.BaseAddress,
					XSPIPS_SR_OFFSET);
			if ( StatusReg & XSPIPS_IXR_MODF_MASK )
			{
				/*
				 * Clear the mode fail bit
				 */
				XSpiPs_WriteReg(
					InstancePtr->Config.BaseAddress,
					XSPIPS_SR_OFFSET,
					XSPIPS_IXR_MODF_MASK);			
				return 1;
			}
		} while ((StatusReg & XSPIPS_IXR_TXOW_MASK) == 0);

		/*
		 * A transmit has just completed. Process received data and
		 * check for more data to transmit.
		 * First get the data received as a result of the transmit
		 * that just completed. Receive data based on the
		 * count obtained while filling tx fifo. Always get the
		 * received data, but only fill the receive buffer if it
		 * points to something (the upper layer software may not
		 * care to receive data).
		 */
		while (TransCount) {
			u8 TempData;
			TempData = XSpiPs_RecvByte(
				InstancePtr->Config.BaseAddress);
			if (InstancePtr->RecvBufferPtr != NULL) {
				*InstancePtr->RecvBufferPtr++ = (u8) TempData;
			}
			InstancePtr->RequestedBytes--;
			--TransCount;
		}
	}

	/*
	 * Clear the slave selects now, before terminating the transfer.
	 */
	if (XSpiPs_IsManualChipSelect(InstancePtr)) {
		ConfigReg = XSpiPs_ReadReg(InstancePtr->Config.BaseAddress,
					XSPIPS_CR_OFFSET);
		ConfigReg |= XSPIPS_CR_SSCTRL_MASK;
		XSpiPs_WriteReg(InstancePtr->Config.BaseAddress,
				 XSPIPS_CR_OFFSET, ConfigReg);
	}
	/*
	 * Clear the busy flag.
	 */
	InstancePtr->IsBusy = 0;

	/*
	 * Disable the device.
	 */
	XSpiPs_Disable(InstancePtr);

	return 0;
}

void delay_SL( u32 delayCount )
{
	do{
		__asm__("nop");
		delayCount--;
	}while(delayCount>0);
}

int writeBCM5396( XSpiPs *Spi_ptr,u8 page, u8 offset, u8 *pBuffer )
{
	u8 data[20];
	u32 retVal;
	u32 u32SendNum, u32ReqRetNum;
	int i;

	for(i=0;i<20;i++)
		data[i]=i;

	// Set Page
	data[0] = NWRITE;
	data[1] = SPG;
	data[2] = page;
	u32SendNum = 3;
	u32ReqRetNum = 0;

	retVal = XSpiPs_PolledTransfer(Spi_ptr,data,NULL,u32SendNum);//pBuffer
	if( retVal != 0 )
	{
		printf("Call XSpiPs_PT_SL 1 Failed\n\r");
	}

	// Read STS
READ_STS_1:
	data[0] = NREAD;
	data[1] = STS;
	u32SendNum = 2;
	u32ReqRetNum = 1;
	retVal = XSpiPs_PolledTransfer(Spi_ptr,data,NULL,u32SendNum+u32ReqRetNum);
	/*
	xil_printf("STS 1 [%d]\n\r", retVal );
	for( u8Idx=0; u8Idx<u32ReqRetNum; u8Idx++ )
		xil_printf("%02x ", workBuf[u8Idx] );
	xil_printf("\n\r");
*/	
	if( retVal == 0 )
	{
		if((pBuffer[2] & SPIF)==0)//( workBuf[2] & SPIF )
		{			
			// Set Page
			data[0] = NWRITE;
			data[1] = SPG;
			data[2] = page;
			u32SendNum = 3;
			u32ReqRetNum = 0;
			retVal = XSpiPs_PolledTransfer(Spi_ptr,data,NULL,u32SendNum+u32ReqRetNum);
			if( retVal != 0 )
			{
				printf("Call XSpiPs_PT_SL 1 Failed\n\r");
			}

			// Write Data
			data[0] = NWRITE;
			data[1] = offset;
			data[2] = pBuffer[0];
			data[3] = pBuffer[1];
			u32SendNum = 4;
			u32ReqRetNum = 0;//1;

			retVal = XSpiPs_PolledTransfer(Spi_ptr,data,NULL,u32SendNum+u32ReqRetNum);
			if( retVal != 0 )
			{
				printf("Call XSpiPs_PT_SL 2 Failed\n\r");
			}

		}
		else
		{
			retVal = 1;
			printf( "writeBCM5396 Timeout 1 Occured!\n" );
			delay_SL(0x100000);

			// Set Page
			data[0] = NWRITE;
			data[1] = SPG;
			data[2] = page;
			u32SendNum = 3;
			u32ReqRetNum = 0;
			
			retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
			if( retVal != 0 )
			{
				printf("Call XSpiPs_PT_SL 1 Failed\n\r");
			}

			goto READ_STS_1;
		}
	}
	else
		printf("Call XSpiPs_PT_SL 4 Failed\n");

	return retVal;
}

int readBCM5396(XSpiPs *Spi_ptr, u8 page, u8 offset, u8 *pBuffer )
{
	u8 data[8];
	u32 retVal;
	u32 u32SendNum, u32ReqRetNum;


	// Set Page
	/*
	bcmCmd.cmd = NWRITE;
	bcmCmd.regAdrs = SPG;
	data[0] = page;
	*/
	data[0] = NWRITE;
	data[1] = SPG;
	data[2] = page;

	u32SendNum = 3;
	u32ReqRetNum = 0;

	retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum);
	if( retVal != XST_SUCCESS )
	{
		printf("Call XSpiPs_PT_SL 1 Failed\n\r");
	}
	// Read STS
READ_STS_1:
/*
	bcmCmd.cmd = NREAD;
	bcmCmd.regAdrs = STS;
	*/
    	data[0] = NREAD;
	data[1] = STS;
	u32SendNum = 2;
	u32ReqRetNum = 1;

	retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
/*
	printf("STS 1 [%d]\n\r", retVal );
	for( i=0; i<u32ReqRetNum; i++ )
		printf("%02x ", pBuffer[i] );
	printf("\n\r");
*/
	//
	if( retVal == XST_SUCCESS )
	{
		if( (pBuffer[2] & SPIF)==0 )
		{
			// Set Page
			/*
			bcmCmd.cmd = NWRITE;
			bcmCmd.regAdrs = SPG;
			data[0] = page;
			*/
			data[0] = NWRITE;
			data[1] = SPG;
			data[2] = page;
			u32SendNum = 3;
			u32ReqRetNum = 0;

			retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
			if( retVal != XST_SUCCESS )
			{
				printf("Call XSpiPs_PT_SL 1 Failed\n\r");
			}

			// Set Offset in Page, "Null" operation :)
			/*
			bcmCmd.cmd = NREAD;
			bcmCmd.regAdrs = offset;
			*/
			data[0] = NREAD;
			data[1] = offset;
			u32SendNum = 2;
			u32ReqRetNum = 1;

			retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
			if( retVal != XST_SUCCESS )
			{
				printf("Call XSpiPs_PT_SL 2 Failed\n\r");
			}


			// Read STS
READ_STS_2:
/*
			bcmCmd.cmd = NREAD;
			bcmCmd.regAdrs = STS;
			*/
   		        data[0] = NREAD;
	        	data[1] = STS;
			u32SendNum = 2;
			u32ReqRetNum = 1;

			retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
/*
			
			for( u8Idx=0; u8Idx<u32ReqRetNum; u8Idx++ )
				xil_printf("%02x ", pBuffer[u8Idx] );
			xil_printf("\n\r");
*/
			if( retVal == XST_SUCCESS )
			{
				if( pBuffer[2] & RACK )
				{
					
					data[0] = NREAD;
				    	data[1] = SIO;
					u32SendNum = 2;
					u32ReqRetNum = 4;
					
					retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
					/*
					xil_printf("DATA [%d]\n\r", retVal );
					for( u8Idx=0; u8Idx<u32ReqRetNum; u8Idx++ )
						xil_printf("%02x ", pBuffer[u8Idx] );
					xil_printf("\n\r");
*/
				}
				else
				{
					retVal = XST_FAILURE;
					printf( "Timeout 2 Occured!\n\r" );
					delay_SL(0x100000);
					// Set Page					
					data[0] = NWRITE;
					data[1] = SPG;
					data[2] = page;
					u32SendNum = 3;
					u32ReqRetNum = 0;
					//retVal = XSpiPs_PT_SL(Spi_ptr, data, pBuffer, u32SendNum, &u32ReqRetNum );
					retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
					if( retVal != XST_SUCCESS )
					{
						printf("Call XSpiPs_PT_SL 1 Failed\n");
					}

					goto READ_STS_2;
				}

			}
			else
				printf("Call XSpiPs_PT_SL 3 Failed\n\r");
		}
		else
		{
			retVal = XST_FAILURE;
			printf( "Timeout 1 Occured!\n" );
			delay_SL(0x100000);

			// Set Page			
			data[0] = NWRITE;
			data[1] = SPG;
			data[2] = page;
			u32SendNum = 3;
			u32ReqRetNum = 0;

			retVal = XSpiPs_PolledTransfer(Spi_ptr,data,pBuffer,u32SendNum+u32ReqRetNum);
			if( retVal != XST_SUCCESS )
			{
				printf("Call XSpiPs_PT_SL 1 Failed\n");
			}

			goto READ_STS_1;
		}
	}
	else
		printf("Call XSpiPs_PT_SL 4 Failed\n");

	return retVal;
}


void myspi_init()
{
	XSpiPs Spi;
	int i32Option,status;
	int i;
	unsigned char workBuf[10];

	workBuf[0]=0xf0;
	workBuf[1]=0x1;

	g_SpiConfig = &XSpiPs_ConfigTable[0];

	//Initialize the SPI device.
	Spi.IsBusy = 0;

	Spi.Config.BaseAddress = g_SpiConfig->BaseAddress;
	//Spi.StatusHandler = StubStatusHandler;

	Spi.SendBufferPtr = NULL;
	Spi.RecvBufferPtr = NULL;
	Spi.RequestedBytes = 0;
	Spi.RemainingBytes = 0;
	Spi.IsReady = 0x11111111;

	XSpiPs_WriteReg(Spi.Config.BaseAddress,0,0x00020000);
	//Initialize the SPI device. end
	
	i32Option = 0x1 | 0x2 | 0x4 | 0x10;
	Spi_SetOptions(&Spi, i32Option);

	XSpiPs_SetSlaveSelect(&Spi,1 );

	XSpiPs_SetClkPrescaler(&Spi, 6 );

	workBuf[0]=0xf0;
	workBuf[1]=0x1;
	workBuf[2]=0;
	for(i=0x10;i<=0x1f;i++)
	{
		status = writeBCM5396(&Spi, i, 0x20, workBuf );
	}
/*
	printf("*****************Read SPI Reg of 5396******************\r\n");
	printf("---BMC Status Registers(PAGE 0x10-0x1F)---\n");
	for(i=0x10;i<=0x1f;i++)
	{
		  status = readBCM5396(&Spi, i, 0x28, workBuf );
		  printf("port=%d,offset=0x28,data=0x%x\n",(i-0x10),workBuf[2]);
		  printf("port=%d,offset=0x29,data=0x%x\n",(i-0x10),workBuf[3]);
    	}

	printf("---BMC Status Registers(PAGE 0x10-0x1F)---\n\r");
	for(i=0x10;i<=0x1f;i++)
	{
		  status = readBCM5396(&Spi, i, 0x20, workBuf );
		  printf("port=%d,offset=0x20,data=0x%x\n\r",(i-0x10),workBuf[2]);
	
		  printf("port=%d,offset=0x21,data=0x%x\n\r",(i-0x10),workBuf[3]);
    	}
*/

}

